package servlets;

import dataTypes.*;

import java.sql.Time;
import java.util.Collections;
import net.sf.jsr107cache.Cache;
import net.sf.jsr107cache.CacheException;
import net.sf.jsr107cache.CacheFactory;
import net.sf.jsr107cache.CacheManager;
import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Properties;
import java.util.TimeZone;
import java.util.TreeMap;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import javax.jdo.Transaction;
import javax.mail.*;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.omg.CORBA.NameValuePair;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.memcache.jsr107cache.GCacheFactory;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;

public class appManager implements Serializable {

	private static final long serialVersionUID = 8404734948518828261L;
	private static appManager instance;
	private static List<deal> activedeals;
	public static List<userDevice> users;
	private static Cache cache;
	private static String authToken;

	public static appManager getInstance() {
		if (instance == null) {
			instance = new appManager();
		}
		return instance;
	}

	private appManager() {
		String[] dealsFilter = { "isActive == true" };
		String[] userFilter = { "deviceRegistrationID != null" };

		try {
			Map props = new HashMap();
			props.put(GCacheFactory.EXPIRATION_DELTA, 86400);// set cached item
																// expiration
																// date to 24
																// hours
			CacheFactory cacheFactory = CacheManager.getInstance()
					.getCacheFactory();
			cache = cacheFactory.createCache(Collections.emptyMap());
		} catch (CacheException e) {
			e.printStackTrace();
		}

		this.activedeals = getDealsFromDb(dealsFilter, 1);
		this.users = getUsersFromDb(userFilter, 1);
		// C2DMRegisterWithGoogle.register();
		this.authToken = getAuthTokenFromDB();
	}

	private String getAuthTokenFromDB() {
		for (userDevice usr : users) {
			if (usr.getGoogleName().equals("OURACCOUNT"))
				return this.authToken = usr.getDeviceRegistrationID();
		}
		return null;
	}

	public List<userDevice> getUsersFromDb(String[] filters, int numOfFilters) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query q = pm.newQuery(userDevice.class);
		ArrayList<userDevice> result = new ArrayList<userDevice>();
		for (int i = 0; i < numOfFilters; i++) {
			q.setFilter(filters[i]);
		}
		try {
			List<userDevice> results = (List<userDevice>) q.execute();
			for (userDevice d : results) {
				userDevice d1 = pm.detachCopy(d);
				result.add(d1);
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			q.closeAll();
			pm.close();

		}
		return result;
	}

	public List<userDevice> getUsers() {
		return users;
	}

	public void addUsertoDB(String uId, String deviceRegistrationId,
			String deviceId) {
		userDevice dbuser = null;
		userDevice localuser = null;
		for (userDevice u : users) {
			if (u.getGoogleName().equals(uId)) {
				localuser = u;
			}
		}
		if (localuser != null) {// try to use cached users
			if (localuser.getDeviceRegistrationID() == deviceRegistrationId
					|| deviceRegistrationId == null) {
				return;
			}// if deviceregid already stored then return
			else if (deviceRegistrationId != null) {// else write to db and
													// cache
				PersistenceManager pm = PMF.get().getPersistenceManager();
				try {

					dbuser = pm.getObjectById(
							userDevice.class,
							KeyFactory.createKey(
									userDevice.class.getSimpleName(), uId));
				} catch (Exception e) {

				}
				if (dbuser == null) {// if true there's a lack of consistency
										// between cache and db
					dbuser = new userDevice(uId, null, null);
				}
				dbuser.setdeviceRegistrationID(deviceRegistrationId);
				dbuser.setdeviceID(deviceId);
				pm.makePersistent(dbuser);
				pm.close();
				localuser.setdeviceID(deviceId);
				localuser.setdeviceRegistrationID(deviceRegistrationId);
				return;
			}
		} else {// no cached user with this Id
			PersistenceManager pm = PMF.get().getPersistenceManager();
			try {

				dbuser = pm.getObjectById(userDevice.class, KeyFactory
						.createKey(userDevice.class.getSimpleName(), uId));
			} catch (Exception e) {

			}
			if (dbuser != null) {// if already in database
				if (deviceRegistrationId == null) {
					pm.close();// nothing to update
					return;
				}
				if (dbuser.getDeviceRegistrationID() == deviceRegistrationId) {// user
																				// already
																				// registered
																				// to
					pm.close();															// c2dm
					return;
				}

			} else {// user doesn't exists in db
				dbuser = new userDevice(uId, null, null);
			}
			dbuser.setdeviceRegistrationID(deviceRegistrationId);// if we got
																	// here we
																	// need to
																	// update
																	// the
																	// existing/newly
																	// created
																	// user
																	// fields
			dbuser.setdeviceID(deviceId);

			pm.makePersistent(dbuser);
	
			if (dbuser != null && dbuser.getDeviceRegistrationID() != null) {// update
																				// cache
				users.add(pm.detachCopy(dbuser));
			}
			pm.close();

		}
	}

	public List<deal> getDeals() {
		for (deal d : activedeals) {
			if (beforeCurrentTime(d.getEnd())) {
				d.setActive(false);
			}
		}
		ListIterator<deal> iter = activedeals.listIterator();
		while (iter.hasNext()) {
			deal d = iter.next();
			if (!d.isActive()) {
				updateUsersRating(d.getKey());
				iter.remove();
			}
		}
		return activedeals;
	}

	public String getAuthToken() {
		if (authToken != null)
			return authToken;
		else
			return getAuthTokenFromDB();
	}

	private boolean beforeCurrentTime(Date time1) {// return true if time1 is
													// past time2
		Date curr = new Date();
		curr.setTime(curr.getTime() + 10800000);
		return time1.before(curr);
	}

	public userDevice getUserbyName(String uId) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Key userKey = KeyFactory.createKey(userDevice.class.getSimpleName(),
				uId);
		userDevice user1 = pm.getObjectById(userDevice.class, userKey);
		userDevice user2 = pm.detachCopy(user1);
		pm.close();
		return user2;

	}

	public deal getDeal(int bId,int dId){
		PersistenceManager pm=PMF.get().getPersistenceManager();
		Key bKey=KeyFactory.createKey(business.class.getSimpleName(),bId);
		Key dKey=KeyFactory.createKey(bKey,deal.class.getSimpleName(), dId);
		return pm.detachCopy(pm.getObjectById(deal.class,dKey));
		
	}
	
	private void updateUsersRating(Key dk) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		// pm.currentTransaction().begin();
		deal d = (deal) pm.getObjectById(deal.class, dk);
		d.setActive(false);

		for (activeUserDeals coupon : d.getCoupons()) {
			coupon.setActive(false);
			userDevice user = getUserbyName(coupon.getUser());
			user.setRating((1 - ((user.numTaken - user.numUsed) / user.numTaken)) * 100);
			pm.makePersistent(user);
		}
		// pm.currentTransaction().commit();
		pm.close();

	}
	
	public List<deal> getDealbyBusiness(int bId){
		Key bKey=KeyFactory.createKey(business.class.getSimpleName(),bId);
		PersistenceManager pm=PMF.get().getPersistenceManager();
		business b=pm.getObjectById(business.class,bKey);
		ArrayList<deal> listofdeals = new ArrayList<deal>();
		for (deal d:b.getDeals()){
			listofdeals.add(pm.detachCopy(d));
		}
		if (listofdeals.size()==0){
			return null;
		}
		pm.close();
		return listofdeals;
	}

	public List<deal> getDealsFromDb(String[] filters, int numOfFilters) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query q = pm.newQuery(deal.class);
		ArrayList<deal> result = new ArrayList<deal>();
		for (int i = 0; i < numOfFilters; i++) {
			q.setFilter(filters[i]);
		}
		try {
			List<deal> results = (List<deal>) q.execute();
			for (deal d : results) {
				deal d1 = pm.detachCopy(d);
				result.add(d1);
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			q.closeAll();
			pm.close();

		}
		return result;

	}

	public String businessReg(business b) {
		String result = null;
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			pm.makePersistent(b);
			result = "Your Business ID is: " + b.getKey().getId();

		} catch (Exception e) {
			result = "Could not register as a busuness.";

		} finally {
			pm.close();
		}
		return result;
	}

	public long[] userAdd(int bId, String uId, int dId, int num) {
		
		long[] result=new long[2];
		result[0]=0;
		result[1]=-1;
		Key bk = KeyFactory.createKey(business.class.getSimpleName(), bId);
		Key dk = KeyFactory.createKey(bk, deal.class.getSimpleName(), dId);
		int numtakenbyuser=numCouponsUserTakenPerDeal(uId, dk);
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			
			userDevice user = getUserbyName(uId);
			if (user.rating < 0) {
				return result;
			}
			pm.currentTransaction().begin();
			business b = pm.getObjectById(business.class, bk);
			for (deal d : b.getDeals()) {
				if (d.getKey().equals(dk)) {
					if (!beforeCurrentTime(d.getEnd())) {
						if (d.getNumOfCoupons() > d.getNumPurchased()&&(d.getMaxPerUser()>numtakenbyuser)) {

							activeUserDeals a = new activeUserDeals(uId, num,
									d, false);
							d.addCoupon(a);
							a.generateCouponCode();
							pm.currentTransaction().commit();

							result[0] = a.getcouponcode();
							result[1]=d.getAmount()-d.getPercent();
							// update user numTaken
							user.setNumTaken(user.getNumTaken() + num);
							for (deal locald : activedeals) {
								if (locald.compareTo(d) == 0) {
									locald.setNumPurchased(locald
											.getNumPurchased() + 1);
									break;
								}
							}
							pm.makePersistent(user);
							
							break;
						
							
						}
						if (d.getMaxPerUser()<=numtakenbyuser){
							result[0]=1;
							result[1]=-1;
						}
					}
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (pm.currentTransaction().isActive())
				pm.currentTransaction().rollback();
			pm.close();
			
		}
		return result;
	}

	public long businessLog(String username, String password) {
		long ret = -1;
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query q = pm.newQuery(dataTypes.business.class);
		try {
			q.setFilter("userName == '" + username + "'");
			List<business> result = (List<business>) q.execute();
			if (result.get(0).getPassword().trim().toLowerCase()
					.equals(sha1.SHA1(password).trim().toLowerCase()))
				ret = result.get(0).getKey().getId();
		} catch (Exception e) {

		} finally {
			pm.close();
		}
		return ret;
	}

	public Boolean sendEmail(String googleName, Long id,long moneysaved) {
		boolean sent = false;
		Properties props = new Properties();
		Session session = Session.getDefaultInstance(props, null);

		String msgBody = id.toString();

		try {
			Message msg = new MimeMessage(session);
			msg.setFrom(new InternetAddress("couponnowmobile@gmail.com"));
			msg.addRecipient(Message.RecipientType.TO, new InternetAddress(
					googleName, "Mr. " + googleName));
			msg.setSubject("CoupoNow - You Have A New DEAL!");
			
			//msg.setText("Hello! Your deal number is:" + id);
			msg.setContent
	          ("<img src='http://coupon-now.appspot.com/heading1.png' width='400px' height='120px'><BR>" +
	                  "<h1 style='color:red'>!מזל טוב"  +"<BR> ! כרגע חסכת "+moneysaved+" ש\"ח! </h1>" + 
	                  "<BR><b>" + " מספר הקופון שלך הוא:  " + id + "</b>", "text/html"
);
			Transport.send(msg);

		} catch (AddressException e) {

		} catch (MessagingException e) {
			// ...
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return true;

	}

	public Boolean addCoupon(int bId, int num, int max, int type, int full,
			int percent, int sTimeH, int sTimeM, int eTimeH, int eTimeM) {
		Date curr = new Date();
		curr.setTime(curr.getTime() + 10800000);
		Date start = new Date(curr.getYear(), curr.getMonth(), curr.getDate(),
				sTimeH, sTimeM);
		Date end = new Date(curr.getYear(), curr.getMonth(), curr.getDate(),
				eTimeH, eTimeM);
		Key bkey = KeyFactory.createKey(business.class.getSimpleName(), bId);
		PersistenceManager pm = PMF.get().getPersistenceManager();
		business b = pm.getObjectById(business.class, bkey);
		deal newDeal = new deal(num, max, type, full, percent, true, start, end);
		boolean ret = false;
		try {
			pm.currentTransaction().begin();
			b.getDeals().add(newDeal);
			pm.currentTransaction().commit();
			activedeals.add(pm.detachCopy(pm.detachCopy(newDeal)));
			ret = true;
		} catch (Exception e) {
			e.printStackTrace();

		} finally {
			pm.close();
		}
		return ret;
	}

	public deal validateCoupon(int bId, long num) {
		boolean ret = false;
		deal d = null;
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Key bKey = KeyFactory.createKey(business.class.getSimpleName(), bId);
		try {
			pm.currentTransaction().begin();
			Query q = pm.newQuery(activeUserDeals.class);
			q.setFilter("couponcode == " + num);
			List<activeUserDeals> result = (List<activeUserDeals>) q.execute();
			if (!result.isEmpty()) {

				business b = pm.getObjectById(business.class, bId);
				activeUserDeals coup = result.get(0);
				d = result.get(0).getDeal();
				if (coup.getClass().equals(activeUserDeals.class)) {
					if (b.getKey().equals(d.getBusiness().getKey())) {
						if ((!coup.isDidUse()) && d.isActive()) {
							result.get(0).setDidUse(true);
							result.get(0).setActive(false);
							result.get(0).setTimeUsed(new Date());
							ret = true;
							d.setNumUsed(d.getNumUsed() + 1);
						}

					}
				}
				if (!ret) {
					pm.currentTransaction().rollback();
					return null;
				}
				pm.currentTransaction().commit();
				Key gName = KeyFactory.createKey(
						userDevice.class.getSimpleName(), coup.getUser());
				userDevice u = pm.getObjectById(userDevice.class, gName);
				u.setNumUsed(u.getNumUsed() + coup.getNumPurchased());
				pm.makePersistent(u);
				for (deal locald : activedeals) {
					if (locald.compareTo(d) == 0) {
						locald.setNumUsed(locald.getNumUsed() + 1);
						break;
					}
				}

			}
		} catch (Exception e) {

		} finally {
			if (pm.currentTransaction().isActive()) {
				pm.currentTransaction().rollback();
			}
			pm.close();
		}
		if (ret)
			return d;
		else
			return null;
	}

	public List<deal> getActiveDealsByBusiness(int bId) {
		Key bKey = KeyFactory.createKey(business.class.getSimpleName(), bId);
		List<deal> results = new ArrayList<deal>();
		for (deal d : getDeals()) {
			if (d.getBusiness().getKey().equals(bKey)) {
				results.add(d);
			}
		}
		return results;

	}

	public deal constructDeal(business b, int num, int max, int type, int full,
			int percent, int sTimeH, int sTimeM, int eTimeH, int eTimeM) {

		Date curr = new Date();
		curr.setTime(curr.getTime() + 10800000);
		Date start = new Date(curr.getYear(), curr.getMonth(), curr.getDate(),
				sTimeH, sTimeM);
		Date end = new Date(curr.getYear(), curr.getMonth(), curr.getDate(),
				eTimeH, eTimeM);
		deal newDeal = new deal(num, max, type, full, percent, true, start, end);

		return newDeal;
	}

	public String getGoogleName(HttpServletRequest req) {
		String googleName;
		if (req.getUserPrincipal() == null)
			return null;
		googleName = req.getUserPrincipal().getName();
		return googleName;
	}

	public void signIn(HttpServletRequest req, HttpServletResponse resp) {

		String returnUrl = req.getRequestURI() + '?' + req.getQueryString();
		try {
			resp.sendRedirect("/googleuserservlet?" + returnUrl);
		} catch (IOException e) {

		}

	}

	public void setPrefs(String location, int type, int maxNumOfNotifications,
			String googleName) {
		Key gName = KeyFactory.createKey(userDevice.class.getSimpleName(),
				googleName);
		PersistenceManager pm = PMF.get().getPersistenceManager();

		userDevice u = (userDevice) pm.getObjectById(userDevice.class, gName);

		u.setLocation(location);
		u.setMaxNumOfNotification(maxNumOfNotifications);
		pm.close();

	}

	public boolean isBusiness(int bId) {
		boolean ret = false;
		PersistenceManager pm = PMF.get().getPersistenceManager();
		business b = null;
		try {
			b = pm.getObjectById(business.class,
					KeyFactory.createKey(business.class.getSimpleName(), bId));
			if (b != null)
				ret = true;
		} catch (Exception e) {

		} finally {
			pm.close();
		}
		return ret;

	}

	public int numCouponsUserTakenPerDeal(String gName,Key dKey){
		int counter=0;
		PersistenceManager pm = PMF.get().getPersistenceManager();
		deal d=pm.getObjectById(deal.class,dKey);
		List<activeUserDeals>coupons =d.getCoupons();
		for (activeUserDeals coup:coupons){
			if (coup.getUser().equalsIgnoreCase(gName)){
				counter++;
			}
		}
		pm.close();
		return counter;
		
	}
	
	public Map<deal, List<activeUserDeals>> getUserCoupons(String gName) {

		Map<deal, List<activeUserDeals>> DealsnCoupons = new TreeMap<deal, List<activeUserDeals>>();
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query q = pm.newQuery(activeUserDeals.class);
		q.setFilter("active == true && googleName == '" + gName + "'");
		List<activeUserDeals> coupons = (List<activeUserDeals>) q.execute();
		for (activeUserDeals a : coupons) {
			deal d = pm.detachCopy(a.getDeal());
			if (DealsnCoupons.containsKey(d)) {
				DealsnCoupons.get(d).add(a);
			} else {
				List<activeUserDeals> lst = new ArrayList<activeUserDeals>();
				lst.add(a);
				DealsnCoupons.put(d, lst);
			}
		}
		return DealsnCoupons;

	}

	public void resetUserRating() {
		PersistenceManager pm=PMF.get().getPersistenceManager();
		Query q=pm.newQuery(userDevice.class);
		q.setFilter("rating == 0");
		List<userDevice> users=(List<userDevice>) q.execute();
		for (userDevice u:users){
				u.setRating(10);
		}
			
	}

}